package com.matthewmadson.util.configurator;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.cli.ParseException;

import com.google.common.collect.Maps;

public class ConfigFileManager
{
	private static ConfigFileManager instance;
	private Map<String,InputStream> streams;
	private Map<String,Properties> loadedProperties;
	
	private ConfigFileManager() 
	{
		this.streams = Maps.newHashMap();
		this.loadedProperties = Maps.newHashMap();
	}
	
	public static ConfigFileManager getManager()
	{
		return instance == null ? new ConfigFileManager() : instance;
	}
	
	public void parse(ConfigFiles configFiles) 
	throws ParseException
	{
		parse(configFiles, new String[1]);
	}

	public void parse(ConfigFiles configFiles, String[] args) 
	throws ParseException
	{
		for(ConfigFile file : configFiles)
		{
			boolean configLoaded = false;
			for(Location location : file.getLocations())
			{
				if(configLoaded)
					break;
				
				switch(location.getType())
				{
					case INTERNAL:
						String resourcePath = "/"+location.getPackageLocation().replaceAll("\\.", "/")+"/"+location.getInternalFileName();
						try
						{
							URL resource = this.getClass().getResource(resourcePath);
							if(resource != null)
							{
								InputStream is = this.getClass().getResourceAsStream(resourcePath);
								this.streams.put(file.getName(), is);
								configLoaded = true;
							}
						}
						catch(Exception e)
						{
							System.err.println("Error loading configuration file from location: "+location);
						}
						break;
					case EXTERNAL:
						String externalPath = location.getExternalLocation();
						try
						{
							File externalFile = new File(externalPath);
							if(externalFile != null && externalFile.exists() && externalFile.canRead())
							{
								this.streams.put(file.getName(), new FileInputStream(externalFile));
								configLoaded = true;
							}
						}
						catch(Exception e)
						{
							System.err.println("Error loading configuration file from location: "+location);
						}
						break;
					case COMMAND_LINE:
						for(int i = 0; i < args.length; i++)
						{
							if(configLoaded)
								break;
							
							String arg = args[i];
							if(arg.replaceAll("-*", "").equalsIgnoreCase(location.getCommandLineFlag()))
							{
								String path = args[i+1];
								try
								{
									File externalFile = new File(path);
									this.streams.put(file.getName(), new FileInputStream(externalFile));
									configLoaded = true;
								}
								catch(Exception e)
								{
									System.err.println("Error loading configuration file from location: "+location+" "+path);
								}
							}
						}
						break;
				}
			}
			
			if(!configLoaded && file.isRequired())
				throw new ParseException("Unable to load required configuration file: "+file);
		}
	}

	public Properties getProperties(final String name) 
	throws IOException 
	{
		if(this.loadedProperties.containsKey(name))
			return this.loadedProperties.get(name);
		
		Properties p = new Properties();
		p.load(streams.get(name));
		this.loadedProperties.put(name, p);
		return p;
	}

	public InputStream getInputStream(final String name)
	{
		return this.streams.get(name);
	}

	public boolean isLoaded(final String name)
	{
		return this.streams.containsKey(name) || this.loadedProperties.containsKey(name);
	}

}
